/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstring>
#include <iostream>
#include <vector>
#include <string>

#include "net_http.h"
#include "netstack_hash_map.h"
#include "netstack_log.h"
#include "net_http_inner_types.h"
#include "http_client_request.h"
#include "http_client.h"
#include "http_client_constant.h"
#include "netstack_common_utils.h"

using namespace OHOS::NetStack::HttpClient;

std::mutex requestMutex;
static std::unordered_map<uint32_t, std::shared_ptr<HttpClientTask>> cppRequestTask;

Http_Headers *OH_Http_CreateHeaders(void)
{
    Http_Headers *headers = (Http_Headers *)malloc(sizeof(Http_Headers));
    if (headers == nullptr) {
        NETSTACK_LOGE("failed to alloc for headers");
        return nullptr;
    }
    headers->fields = CreateMap();
    if (headers->fields == nullptr) {
        NETSTACK_LOGE("failed to alloc memory for headerMap");
        free(headers);
        headers = nullptr;
    }

    return headers;
}

static void OH_Http_DestroyHeaderValue(void *value)
{
    Http_HeaderValue *headerValue = (Http_HeaderValue *)value;
    Http_HeaderValue *next;
    while (headerValue != nullptr) {
        next = headerValue->next;
        free(headerValue->value);
        headerValue->value = nullptr;
        free(headerValue);
        headerValue = next;
    }
}

void OH_Http_DestroyHeaders(Http_Headers **headers)
{
    if (headers == nullptr) {
        return;
    }
    if (*headers == nullptr) {
        return;
    }
    if ((*headers)->fields != nullptr) {
        Netstack_DestroyMapWithValue((*headers)->fields, OH_Http_DestroyHeaderValue);
        (*headers)->fields = nullptr;
    }
    free(*headers);
    *headers = nullptr; // 防止悬空指针
}

static char *OH_Http_ToLowerCase(const char *str)
{
    if (str == nullptr) {
        return nullptr;
    }
    char *lowerStr = strdup(str);
    if (lowerStr == nullptr) {
        NETSTACK_LOGE("failed to alloc memory for string");
        return nullptr;
    }
    size_t len = strlen(lowerStr);
    for (size_t i = 0; i < len; i++) {
        lowerStr[i] = static_cast<char>(tolower(static_cast<int>(lowerStr[i])));
    }
    return lowerStr;
}

// copy both name and value
uint32_t OH_Http_SetHeaderValue(Http_Headers *headers, const char *name, const char *value)
{
    if (headers == nullptr || headers->fields == nullptr || name == nullptr || value == nullptr) {
        return OH_HTTP_PARAMETER_ERROR;
    }
 
    char *lowerName = OH_Http_ToLowerCase(name);
    if (lowerName == nullptr) {
        return OH_HTTP_OUT_OF_MEMORY;
    }
    Http_HeaderValue *existValue = (Http_HeaderValue *)Netstack_GetMapEntry(headers->fields, lowerName);
    Http_HeaderValue *previous = nullptr;
    while (existValue != nullptr) {
        if (strcmp(existValue->value, value) == 0) {
            free(lowerName);
            return OH_HTTP_RESULT_OK;
        }
        previous = existValue;
        existValue = existValue->next;
    }

    Http_HeaderValue *headerValue = (Http_HeaderValue *)calloc(1, sizeof(Http_HeaderValue));
    if (headerValue == nullptr) {
        NETSTACK_LOGE("failed to alloc memory for header value");
        free(lowerName);
        return OH_HTTP_OUT_OF_MEMORY;
    }
    headerValue->value = strdup(value);
    if (headerValue->value == nullptr) {
        free(headerValue);
        free(lowerName);
        return OH_HTTP_OUT_OF_MEMORY;
    }
 
    if (previous == nullptr) {
        uint32_t res = Netstack_PutMapEntry(headers->fields, lowerName, headerValue);
        if (res != OH_HTTP_RESULT_OK) {
            free(headerValue->value);
            free(headerValue);
            free(lowerName);
            return res;
        }
    } else {
        previous->next = headerValue;
    }
    free(lowerName);
    return OH_HTTP_RESULT_OK;
}

Http_HeaderValue *OH_Http_GetHeaderValue(Http_Headers *headers, const char *name)
{
    if (headers == nullptr || headers->fields == nullptr || name == nullptr) {
        return nullptr;
    }
    char *lowerName = OH_Http_ToLowerCase(name);
    if (lowerName == nullptr) {
        return nullptr;
    }
    Http_HeaderValue *value = (Http_HeaderValue *)Netstack_GetMapEntry(headers->fields, lowerName);
    free(lowerName);
    return value;
}

Http_HeaderEntry *OH_Http_GetHeaderEntries(Http_Headers *headers)
{
    if (headers == nullptr || headers->fields == nullptr) {
        NETSTACK_LOGE("OH_Http_GetHeaderEntries headers or headers->fields is null");
        return nullptr;
    }

    uint32_t size = Netstack_GetMapSize(headers->fields);
    if (size == 0) {
        return nullptr;
    }

    Http_HeaderEntry *entries = (Http_HeaderEntry *)calloc(1, sizeof(Http_HeaderEntry) * size);
    if (entries == nullptr) {
        NETSTACK_LOGE("failed to alloc memory for header entries");
        return nullptr;
    }

    auto *mapEntry = Netstack_GetMapFirstEntry(headers->fields);
    uint32_t index = 0;
    while (mapEntry != nullptr && index < size) {
        entries[index].name = static_cast<char *>(Netstack_GetMapKey(mapEntry));
        entries[index].value = static_cast<Http_HeaderValue *>(Netstack_GetMapValue(mapEntry));
        mapEntry = Netstack_GetMapNextEntry(mapEntry);
        index++;
    }

    return entries;
}